#pragma once 

/* 用户表的ODB映射操作 */

#include "mysql.hpp"
#include "user.hxx"
#include "user-odb.hxx"


namespace ChatSystem
{
    //用户表ODB操作
    class UserTable
    {
    public:
        using ptr = std::shared_ptr<ChatSystem::UserTable>;
        UserTable() = delete;
        ~UserTable(){}
        UserTable(const std::shared_ptr<odb::core::database>& db)
        :_db(db)
        {}
        
        //插入用户信息
        bool insert(const std::shared_ptr<ChatSystem::User>& user)
        {
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
            //获取对象开启事务
            odb::transaction trans(_db->begin());
            _db->persist(*user);
            //提交事务
            trans.commit();
            }
            catch(const std::exception& e)
            {
                LOG_ERROR("新增用户 {} 失败, {}", user->nickname(), e.what());
                return false;
            }
            return true;   
        }
        //更新用户信息
        bool update(const std::shared_ptr<User>& user)
        {
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //在进行数据更新的时候，如果直接进行更新会报错，odb中的用法是需要先查询数据再更新数据，
                //是对查询出来的结果user修改后更新，所以说传进来的参数必须是一个查询出来的结果，不能是我们自己构造的；
                _db->update(*user);  
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("更新用户 {} 失败, {}", user->nickname(), e.what());
                return false;
            }
            return true;
        }
        //通过昵称获取用户信息
        std::shared_ptr<User> select_by_nickname(const std::string& nickname)
        {
            std::shared_ptr<User> res;
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //3. 获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //4. 数据操作
                typedef odb::query<User> query;
                typedef odb::result<User> result;
                res = std::shared_ptr<User>(_db->query_one<User>(query::nickname == nickname));
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("通过昵称 {} 查询用户失败, ", nickname, e.what());
            }
            return res;
        }
        //通过手机号获取用户信息
        std::shared_ptr<User> select_by_phone(const std::string& phone)
        {
            std::shared_ptr<User> res;
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //3. 获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //4. 数据操作
                typedef odb::query<User> query;
                typedef odb::result<User> result;
                res = std::shared_ptr<User>(_db->query_one<User>(query::phone == phone));
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("通过手机号 {} 查询用户失败, {}", phone, e.what());
            }
            return res;
        }
        //通过用户ID获取用户信息
        std::shared_ptr<User> select_by_user_id(const std::string& user_id)
        {
            std::shared_ptr<User> res;
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //3. 获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //4. 数据操作
                typedef odb::query<User> query;
                typedef odb::result<User> result;
                res = std::shared_ptr<User>(_db->query_one<User>(query::user_id == user_id));
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("通过用户ID user_id 查询用户失败, {}", user_id, e.what());
            }
            return res;
        }
        //通过多个用户ID获取多个用户信息
        std::vector<User> select_multi_users(const std::vector<std::string>& id_list)
        {
            //需要组织一个SQL 语句去查询： select * from user where id in ('id1', 'id2', .....)
            std::vector<User> res;
            //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
            try
            {
                //3. 获取事务操作对象，开启事务
                odb::transaction trans(_db->begin());
                //4. 数据操作
                typedef odb::query<User> query;
                typedef odb::result<User> result;
                
                std::stringstream ss;
                ss << "where user_id in (";
                for(const auto& id : id_list)
                {
                    ss << "'" << id << "',";
                }
                std::string condition = ss.str();
                condition.pop_back();
                condition +=  ")";
                LOG_DEBUG("过滤条件：{}", condition);
                result r(_db->query<User>(condition));
                for(auto it = r.begin(); it != r.end(); ++it)
                {
                    res.push_back(*it);
                }
                //5. 提交事务
                trans.commit();
            }
            catch(std::exception& e)
            {
                LOG_ERROR("通过用户ID查询多个时用户失败, {}", e.what());
            }
            return res;
        }

    private:
        std::shared_ptr<odb::core::database> _db;   //对数据库的操作都是通过db来操作的

    };
}